#ifndef __UDP__SERVER_HPP
#define __UDP__SERVER_HPP

// 首先写服务端
#include "log.hpp"
#include <iostream>
#include <string>
#include <cerrno>
#include <string>
#include <cstdlib>
#include <strings.h>
#include <cstdio>
#include <unistd.h>
// 网络头文件四件套
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define SIZE 1024

class UdpServer
{
public:
    UdpServer(uint16_t port, std::string ip = "") : _port(port), _ip(ip), _sock(-1)
    {
    }
    bool initServer()
    {
        // 实用网络系统调用，完成网络功能
        // 1.创建套接字
        _sock = socket(AF_INET, SOCK_DGRAM, 0); // 第一个参数确定通信方式，第二个参数确定网络通信是基于流式，还是用户数据报
        if (_sock < 0)
        {
            logMessage(FATAL, "%d:%s", errno, strerror(errno));
            exit(2);
        }
        // 2.bind绑定,套接字需要ip确定唯一性，端口来进行绑定标识该服务器上的唯一进程
        // bind：将用户色湖之的ip和port在内核中和我们当前的进程强关联
        //"192.168.1.3"->点分十进制字符串风格的IP地址
        // 每一个区域取值范围是[0~255]：1字节 ->4个区域
        // 理论上，表示一个IP地址，其实4字节就够了！
        // 点分十进制字符串风格的IP地址<->4字节
        struct sockaddr_in local;
        bzero(&local, sizeof(local)); // 初始化空间，local中有ip和port
        local.sin_family = AF_INET;   // 协议家族,协议自身用什么通信,这里是要和网络绑定，所以用AF_INET
        // 服务器的IP和端口未来也是要发送给对方主机的->先将数据发送给网络！
        local.sin_port = htons(_port); // htons主机序列转成网络序列（网络要求大端）短整数s
        // 1.同上，先要将点分十进制字符串风格的IP地址->4字节
        // 2.4字节主机序列->网络序列
        // 有一套接口，可以一次帮我们做完这件事
        local.sin_addr.s_addr = _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str()); // ip地址不用暴露，INADDR_ANY宏就是0,让服务器可以在工作过程中，可以从任意IP中获取数据
        if (bind(_sock, (struct sockaddr *)&local, sizeof(local)) < 0)
        // 识别返回-1,第二个参数统一标准规定用struct sockaddr*
        {
            logMessage(FATAL, "%d:%s", errno, strerror(errno));
            exit(2);
        }
        logMessage(NORMAL, "init udp server done....%s", strerror(errno));
        // 以上都是初始化，接下来done

        return true;
    }
    void Start()
    {
        // 作为一款网络服务器，永远不退出
        // 服务器启动->进程->常驻进程->永远在内存中存在，除非宕机
        char buffer[SIZE];
        for (;;)
        {
            struct sockaddr_in peer; // 纯输出型参数
            bzero(&peer, sizeof(peer));
            socklen_t len = sizeof(peer); // 输入输出型;所以默认传peer大小
            // 输入:peer缓冲区大小 输出:实际读到的peer大小
            char result[256];
            std::string cmd_echo;
            // start.读取数据
            ssize_t s = recvfrom(_sock, buffer, sizeof(buffer) - 1, 0,
                                 (struct sockaddr *)&peer, &len); // flag默认设为0为阻塞
            if (s > 0)
            {
                buffer[s] = 0; // 目前数据做字符串
                // // 1.输出发送的数据信息
                // // 2.是谁？
                // //------------------测试代码-------------------*
                // uint16_t cli_port = ntohs(peer.sin_port);      // 从网络中来的！需要将网络序列转成主机序列
                // std::string cli_ip = inet_ntoa(peer.sin_addr); // 4字节的网络序列IP->本机序列的点分十进制字符串序列
                // printf("[%s:%d]#%s\n", cli_ip.c_str(), cli_port, buffer);
                //------------------测试代码-------------------/
                // 你发过来的字符串是指令ls -a -l
                if(strcasestr(buffer,"rm")!=nullptr||strcasestr(buffer,"rmdir")!=nullptr)
                {
                    std::string err_message="No,这是非法的！";
                    std::cout<<"No,这是非法的！"<<std::endl;
                    sendto(_sock, err_message.c_str(), err_message.size(), 0, (struct sockaddr *)&peer, len);
                    continue;
                }
                FILE *fp = popen(buffer, "r");
                if (nullptr == fp)
                {
                    logMessage(ERROR, "popen:%d:%s", errno, strerror(errno));
                    continue;
                }
                // 读取到文件的缓冲区中，使用fgets按行从流中读取

                while (fgets(result, sizeof(result), fp) != nullptr)
                {
                    cmd_echo += result;
                }
                fclose(fp);
            
            }
            // 分析处理数据
            // end.写回数据
            //sendto(_sock, buffer, strlen(buffer), 0, (struct sockaddr *)&peer, len);
            sendto(_sock, cmd_echo.c_str(), cmd_echo.size(), 0, (struct sockaddr *)&peer, len);
        }
    }
    ~UdpServer()
    {
        if (_sock >= 0)
            close(_sock);
    }

private:
    // 一个服务器，一般必须需要ip地址和port端口号(16位整数)
    uint16_t _port;
    std::string _ip;
    int _sock; // 创建套接字，可能未来别人要用，所以放在类中
};

#endif